package com.locatech.dynamic.datasource;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;






public final class DynamicDataSource extends AbstractRoutingDataSource implements ApplicationContextAware{  
    
    private static final String DATA_SOURCES_NAME = "targetDataSources";  
  
    private static ApplicationContext applicationContext ;
    @Override  
    protected Object determineCurrentLookupKey() {  
        DataSourceBeanBuilder dataSourceBeanBuilder = DataSourceHolder.getDataSource();  
        System.out.println("----进入determineCurrentLookupKey---"+dataSourceBeanBuilder);  
        if (dataSourceBeanBuilder == null) {  
            return null;  
        }  
        DataSourceBean dataSourceBean = new DataSourceBean(dataSourceBeanBuilder);  
        //查看当前容器中是否存在  
        try { 
        	//得到targetDataSources中Map<Object,Object> 
            Map<Object,Object> map=getTargetDataSources();  
        	//System.out.println(map.keySet());
            synchronized (this) { 
            	//如果不存在，则把DataSourceHolder中的得到dataSourceBean放入TargetDataSources
                if (!map.keySet().contains(dataSourceBean.getBeanName())) {  
                    map.put(dataSourceBean.getBeanName(), createDataSource(dataSourceBean));  
                    super.afterPropertiesSet();//通知spring有bean更新  
                }  
                System.out.println("map.keySet():"+map.keySet());
            }  
            return dataSourceBean.getBeanName();  
        } catch (NoSuchFieldException | IllegalAccessException e) {  
            throw new RuntimeException(); 
        }  
    }  
  
  
    private Object createDataSource(DataSourceBean dataSourceBean) throws IllegalAccessException {  
        //在spring容器中创建并且声明bean  
        ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;  
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory();  
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(BasicDataSource.class);  
        //将dataSourceBean中的属性值赋给目标bean  
        Map<String, Object> properties = getPropertyKeyValues(DataSourceBean.class, dataSourceBean);  
        for (Map.Entry<String, Object> entry : properties.entrySet()) {  
            beanDefinitionBuilder.addPropertyValue((String) entry.getKey(), entry.getValue());  
        } 
       
        beanFactory.registerBeanDefinition(dataSourceBean.getBeanName(), beanDefinitionBuilder.getBeanDefinition());  
        return applicationContext.getBean(dataSourceBean.getBeanName());  
    }  
	//得到已经创建的targetDataSources
    @SuppressWarnings("unchecked")
	private Map<Object, Object> getTargetDataSources() throws NoSuchFieldException, IllegalAccessException {  
    	Class ards = AbstractRoutingDataSource.class;
    	Field field = ards.getDeclaredField(DATA_SOURCES_NAME);//dynamicDataSource.getClass().getDeclaredField(DATA_SOURCES_NAME);  
        field.setAccessible(true);  
        return (Map<Object, Object>) field.get(this);  
    }  
  
  
    private <T> Map<String, Object> getPropertyKeyValues(Class<T> clazz, Object object) throws IllegalAccessException {  
        Field[] fields = clazz.getDeclaredFields();  
        Map<String, Object> result = new HashMap<>();  
        for (Field field : fields) {  
            field.setAccessible(true);  
            result.put(field.getName(), field.get(object));  
        }  
        result.remove("beanName");  
        return result;  
    }  
  
    @Override  
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  
        System.out.println(applicationContext);
        this.applicationContext=applicationContext;  
    }  
    
   
    
    
	
	}
  
